#pragma once
#include "../common/net.hpp"
#include "../common/message.hpp"
#include <set>

namespace jsonrpc
{
    // ProviderManager + DiscoverManager + Provider_Discover_Manager
    namespace server
    {
        // 管理服务 -- (method, vector<provider>)  + (conn, provider)
        class ProviderManager
        {
        public:
            using ptr = std::shared_ptr<ProviderManager>;
            struct Provider
            {
                using ptr = std::shared_ptr<Provider>;
                BaseConnection::ptr _conn;
                std::vector<std::string> _methods;
                Address _host;
                std::mutex _mutex;

                Provider(const BaseConnection::ptr &conn, const Address host)
                    : _conn(conn), _host(host)
                {
                }

                void appendMethod(const std::string &method)
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    _methods.push_back(method);
                }
            };

            // 1.添加服务 (服务注册使用)
            void addProvider(const BaseConnection::ptr &conn, const Address host, const std::string &method)
            {
                // 新服务来了, 添加一下
                Provider::ptr provider;
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    // 维护映射关系(看看是不是已经注册过服务的提供者, 不是就再造一个)
                    auto it = _conns.find(conn);
                    if (it != _conns.end())
                        provider = it->second;
                    else
                    {
                        provider = std::make_shared<Provider>(conn, host);
                        _conns.insert(std::make_pair(conn, provider));
                    }
                    auto &providers = _providers[method];
                    providers.insert(provider);
                }
                // 记录它提供的服务
                provider->appendMethod(method);
            }

            // 2.获取服务者 (服务下线使用)
            Provider::ptr getProvider(const BaseConnection::ptr &conn)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _conns.find(conn);
                if (it != _conns.end())
                    return it->second;
                else
                    return Provider::ptr();
            }

            // 3.删除服务者 (连接断开使用)
            void delProvider(const BaseConnection::ptr &conn)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _conns.find(conn);
                if (it == _conns.end())
                {
                    // 断开连接的不是一个服务提供者
                    return;
                }
                // 删除该连接的提供者 所建立的方法与其的联系
                // 1.找到该提供者及提供的方法, 2.从_providers中删掉 3.从_conns中删掉
                for(auto& method : it->second->_methods)
                {
                    auto& providers = _providers[method];
                    providers.erase(it->second);
                }
                // 删除连接与提供者关系
                _conns.erase(it);
            }

            // 4.根据方法名, 获取提供服务的主机信息
            std::vector<Address> methodHosts(const std::string& method)
            {
                // 找到提供method的providers
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _providers.find(method);
                if(it == _providers.end())
                {
                    ELOG("没有提供该服务的主机");
                    return std::vector<Address>();
                }
                std::vector<Address> ret;
                for(auto& provider : it->second) // std::set<Provider::ptr>
                {
                    ret.push_back(provider->_host);
                }
                return ret;
            }
        private:
            std::mutex _mutex;
            std::unordered_map<std::string, std::set<Provider::ptr>> _providers;
            std::unordered_map<BaseConnection::ptr, Provider::ptr> _conns;
        };

        // 发现管理
        class DiscoverManager
        {
        public:
            using ptr = std::shared_ptr<DiscoverManager>;
            struct Discover
            {
                using ptr = std::shared_ptr<Discover>;
                std::mutex _mutex;
                BaseConnection::ptr _conn;         // 发现者关联的客户连接
                std::vector<std::string> _methods; // 发现的服务名称

                Discover(const BaseConnection::ptr &conn) :_conn(conn){}
                void appendMethod(const std::string &method) 
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    _methods.push_back(method);
                }
            };

            // 1.发现服务 -- 客户端进行服务发现时(新增服务名称)
            Discover::ptr addDiscover(const BaseConnection::ptr &conn, const std::string &method)
            {
                // 维护关系 _discovers + _conns
                // 看是不是已经存在的discover
                Discover::ptr discover;
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _conns.find(conn);
                    if(it != _conns.end())
                    {
                        discover = it->second;
                    }
                    else
                    {
                        discover = std::make_shared<Discover>(conn);
                        _conns.insert(std::make_pair(conn,discover));
                    }
                    auto& discovers = _discovers[method];
                    discovers.insert(discover);
                }
                discover->appendMethod(method);
                return discover;
            }

            // 2.删除服务 -- 发现者客户端断开连接时, 找到发现者信息, 删除关联数据
            void delDiscover(const BaseConnection::ptr &conn) 
            {
                // 1.找到这个发现者及发现的方法 2.从_discovers删除 3.从_conns中删除
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _conns.find(conn);
                if(it == _conns.end())
                {
                    // 断开连接的不是一个发现者
                    return; 
                }
                else
                {
                    for(auto& method : it->second->_methods) // discover->_methods
                    {
                        auto& discovers = _discovers[method];
                        discovers.erase(it->second);
                    }
                }
                _conns.erase(it);
            }
            // 3.服务上线
            void onlineNotify(const std::string &method, const Address& host) 
            {
                notify(method, host, ServiceOptype::SERVICE_ONLINE);
            }
            // 4.服务下线
            void offlineNotify(const std::string &method, const Address& host) 
            {
                notify(method, host, ServiceOptype::SERVICE_OFFLINE);
            }
        private:
            void notify(const std::string &method, const Address& host, ServiceOptype optype)
            {
                // 1.找到该服务的所有发现者 2.进行服务上/下通知(发消息)
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _discovers.find(method);
                if(it == _discovers.end())
                {
                    // 没找到该服务的发现者
                    return;
                }

                auto msg_req = MessageFactory::create<ServiceRequest>();
                msg_req->setHost(host);
                msg_req->setMethod(method);
                msg_req->setId(UUID::uuid());
                msg_req->setMType(MType::REQ_SERVICE);
                msg_req->setOptype(optype);
                for(auto& discover : it->second) // std::set<Discover::ptr>>
                {
                    discover->_conn->send(msg_req); // 进行服务上线/下线通知
                }
            }

        private:
            std::mutex _mutex;
            std::unordered_map<std::string, std::set<Discover::ptr>> _discovers;
            std::unordered_map<BaseConnection::ptr, Discover::ptr> _conns;
        };

        // 整合
        class Provider_Discover_Manager
        {
        public:
            using ptr = std::shared_ptr<Provider_Discover_Manager>;
            // 构造
            Provider_Discover_Manager() 
            :_providers(std::make_shared<ProviderManager>())
            ,_discoverers(std::make_shared<DiscoverManager>())
            {}

            // 1.根据消息类型进行处理(注册给dispatcher) -- 服务操作请求(服务注册, 服务发现)
            void onServiceRequset(const BaseConnection::ptr &conn, const ServiceRequest::ptr &msg) 
            {
                // 服务注册: 新增服务提供者 2.服务上线通知
                // 服务发现: 新增服务发现者
                ServiceOptype optype = msg->optype();
                if(optype == ServiceOptype::SERVICE_REGISTRY)
                {
                    ILOG("%s:%d 注册服务 %s", msg->host().first.c_str(), msg->host().second, msg->method().c_str());
                    _providers->addProvider(conn, msg->host(), msg->method());
                    _discoverers->onlineNotify(msg->method(), msg->host());
                    registryResponse(conn, msg); // 发送响应
                }
                else if(optype == ServiceOptype::SERVICE_DISCOVERY)
                {
                    ILOG("客户端要进行 %s 服务发现！", msg->method().c_str());
                    _discoverers->addDiscover(conn, msg->method());
                    discoveryResponse(conn, msg); // 发送响应
                }
                else
                {
                    ELOG("收到服务操作请求, 但是类型错误");
                    errorResponse(conn, msg); // 发送响应
                }

            }
            // 2.连接关闭的处理
            void onConnShutdown(const BaseConnection::ptr &conn) 
            {
                // 若是提供者就下线对应服务 + 通知, 若是发现者就直接删除
                auto provider = _providers->getProvider(conn);
                if(provider.get() != nullptr)
                {
                    // 找到对应的服务 + 下线
                    ILOG("%s:%d 服务下线", provider->_host.first.c_str(), provider->_host.second);
                    for(auto& method : provider->_methods)
                    {
                        _discoverers->offlineNotify(method, provider->_host);
                    }
                    _providers->delProvider(conn);
                }
                _discoverers->delDiscover(conn);
            }
        private:
        // 服务注册响应
        void registryResponse(const BaseConnection::ptr &conn, const ServiceRequest::ptr &msg)
        {
            auto rsp_msg = MessageFactory::create<ServiceResponse>();
            rsp_msg->setId(msg->rid());
            rsp_msg->setMType(MType::RSP_SERVICE);
            rsp_msg->setOptype(ServiceOptype::SERVICE_REGISTRY);
            rsp_msg->setRCode(RCode::RCODE_OK);
            conn->send(rsp_msg);
        }
        // 服务发现响应
        void discoveryResponse(const BaseConnection::ptr &conn, const ServiceRequest::ptr &msg)
        {
            auto rsp_msg = MessageFactory::create<ServiceResponse>();
            rsp_msg->setId(msg->rid());
            rsp_msg->setMType(MType::RSP_SERVICE);
            rsp_msg->setOptype(ServiceOptype::SERVICE_DISCOVERY);
            rsp_msg->setMethod(msg->method());

            // 1.查找方法的主机地址信息
            std::vector<Address> hosts = _providers->methodHosts(msg->method());
            if(hosts.empty())
            {
                rsp_msg->setRCode(RCode::RCODE_NOT_FOUND_SERVICE);
                conn->send(rsp_msg);
            }
            // 2.找到了就设置信息
            rsp_msg->setHost(hosts);
            rsp_msg->setMethod(msg->method());
            rsp_msg->setRCode(RCode::RCODE_OK);
            conn->send(rsp_msg);
        }
        // 错误响应
        void errorResponse(const BaseConnection::ptr &conn, const ServiceRequest::ptr &msg)
        {
            auto msg_rsp = MessageFactory::create<ServiceResponse>();
            msg_rsp->setId(msg->rid());
            msg_rsp->setMType(MType::RSP_SERVICE);
            msg_rsp->setRCode(RCode::RCODE_INVALID_OPTYPE);
            msg_rsp->setOptype(ServiceOptype::SERVICE_UNKNOW);
            conn->send(msg_rsp);
        }
        
        private:
            ProviderManager::ptr _providers;
            DiscoverManager::ptr _discoverers;
        };
    }
}
